ローカルでのソースコードの変更を自動で検知してKubenetesへデプロイ! Skaffoldを開発モードで使ってみました
はじめに
おはようございます、加藤です。昨日参加した勉強会でSkaffoldの存在を知ったので、早速 ECR & EKS で試してみました。
Skaffoldとは
GoogleContainerTools/skaffold: Easy and Repeatable Kubernetes Development
SkaffoldはGoogleによって開発されている、Kubernetes(以降、k8s)への継続的な開発を容易にするコマンドラインツールです。 開発フェイズでは、アプリケーションのソースコードの変更を検知して、DockerコンテナのBuild→コンテナレジストリへのPush→k8sクラスタにDeployまでを自動で行ってくれます。 本番フェイズでも、パイプライン内でCI/CDを担当させる事が可能です。
このブログでは開発フェイズでSkaffoldをどのように使用するかをターゲットにしています。
前提
- Amazon EKSが構築済みで利用可能であること
- direnvがインストール済みで利用可能であること (AssumeRoleでAWSアカウントにアクセスしている場合)
- assume-roleがインストール済みで利用可能であること (AssumeRoleでAWSアカウントにアクセスしている場合)
やってみた
Skaffoldをインストール
$ brew install skaffold
ECR認証ヘルパーをインストール
ECR認証ヘルパーをインストールします。ECRを利用する場合にはdocker login
接続する方法もありますが、今回は認証ヘルパーを使用します。
awslabs/amazon-ecr-credential-helper: Automatically gets credentials for Amazon ECR on docker push/docker pull
$ brew install docker-credential-helper-ecr
認証ヘルパーに接続先を認識させる為に、Docker configを編集します。カレントディレクトリに.docker/config.json
を作成してください。
account_id, regionは環境に応じて置換してください。AssumeRoleする場合は、AssumeRole先のaccount_idを設定します。
{ "credHelpers": { "[account_id].dkr.ecr.[region].amazonaws.com": "ecr-login" } }
環境変数、その他の設定
環境変数にAWS認証情報やKUBECONFIG, DOCKER_CONFIGを設定します。 また、私は複数のkubectlを利用している関係で、一時的にkubectlにPATHを通しています。
[超小ネタ]direnvを使ってkubectlとkubeconfigをプロジェクト(ディレクトリ)毎に切り替える | DevelopersIO
最終的にこんな風になりました。profile_nameは~/.aws/config
で設定しているプロファイル名です。AssumeRoleしていない人は記載する必要がありません。
eval $(assume-role [profile_name]) export KUBECONFIG="$(pwd)/.kube/config" export DOCKER_CONFIG="$(pwd)/.docker/" PATH_add ~/bin/kubectl/aws/v1.10.3
.envrc
を作成したら下記のコマンドで有効化します。
$ direnv allow
k8sのconfigを取得します。KUBECONFIGを設定しているので、カレントディレクトリの配下に作成されます。
$ aws eks update-kubeconfig --name [cluster_name]
ECRの作成
$ REPOSITORY_URI=$(aws ecr create-repository --repository-name k8s-skaffold/skaffold-example | jq -r '.repository.repositoryUri') $ echo $REPOSITORY_URI
チュートリアル
GitHubリポジトリからサンプルをクローンします。
$ git clone https://github.com/GoogleContainerTools/skaffold $ cd skaffold/examples/getting-started
skaffold.yaml, k8s-pod.yamlを生成します。
$ cat > skaffold.yaml <<EOF apiVersion: skaffold/v1alpha2 kind: Config build: tagPolicy: envTemplate: template: "{{.IMAGE_NAME}}:{{.DIGEST_HEX}}" artifacts: - imageName: ${REPOSITORY_URI} local: deploy: kubectl: manifests: - k8s-* EOF $ cat > k8s-pod.yaml <<EOF apiVersion: v1 kind: Pod metadata: name: getting-started spec: containers: - name: getting-started image: ${REPOSITORY_URI} EOF
下記の2つのファイルが作成されたはずです。
apiVersion: v1 kind: Pod metadata: name: getting-started spec: containers: - name: getting-started image: ${REPOSITORY_URI}
関連するDockerfileとGolangのプログラムも載せておきます。
マルチステージでGolangのプログラムを動かす内容となっていました。
FROM golang:1.10.1-alpine3.7 as builder COPY main.go . RUN go build -o /app main.go FROM alpine:3.7 CMD ["./app"] COPY --from=builder /app .
1秒毎にHello world!
と表示するプログラムでした。
package main import ( "fmt" "time" ) func main() { for { fmt.Println("Hello world!") time.Sleep(time.Second * 1) } }
Skaffoldを開発モードで起動してみます。
$ skaffold dev WARN[0000] config version (skaffold/v1alpha2) out of date: upgrading to latest (skaffold/v1beta2) Starting build... Building [[account_id]].dkr.ecr.[region].amazonaws.com/k8s-skaffold/skaffold-example]... Sending build context to Docker daemon 3.072kB Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder ---> 52d894fca6d4 Step 2/6 : COPY main.go . ---> Using cache ---> 4c7c0ad132bc Step 3/6 : RUN go build -o /app main.go ---> Using cache ---> ac77260c9285 Step 4/6 : FROM alpine:3.7 ---> 9bea9e12e381 Step 5/6 : CMD ["./app"] ---> Using cache ---> 3ee2781f0277 Step 6/6 : COPY --from=builder /app . ---> Using cache ---> 73d583ae6d63 Successfully built 73d583ae6d63 Successfully tagged 2fcda617dc7ad6a6d01f58c0f91ed585:latest The push refers to repository [[account_id]].dkr.ecr.[region].amazonaws.com/k8s-skaffold/skaffold-example] 7f88f55075f4: Preparing d6da3c54c8f3: Preparing d6da3c54c8f3: Layer already exists 7f88f55075f4: Layer already exists 73d583ae6d63ad751f1e4a2cbc2ddacb01c706a7141236cca0ff8b7e7a947447: digest: sha256:da3029816f7614c1b106b605790e6867fb6cb010e6cf9eb9d229dbf5b7cef0ef size: 738 Build complete in 1.216663214s Starting test... Test complete in 14.427µs Starting deploy... kubectl client version: 1.10 kubectl version 1.12.0 or greater is recommended for use with skaffold pod "getting-started" created Deploy complete in 6.574979148s Watching for changes every 1s... [getting-started] Hello world! [getting-started] Hello world! [getting-started] Hello world! [getting-started] Hello world!
コンソールにHello world!
と表示されることを確認できました!
Skaffoldは開発モードで動いていると1秒毎にソースに変更が無いか検知してくれます。
main.go
の10行目を変更してコンソールに表示するメッセージ変更してみましょう、Hello world!2
に変更しました。
[getting-started] Hello world! [getting-started] Hello world! Starting build... Building [[account_id]].dkr.ecr.[region].amazonaws.com/k8s-skaffold/skaffold-example]... Sending build context to Docker daemon 3.072kB Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder ---> 52d894fca6d4 Step 2/6 : COPY main.go . ---> f4bc0451d3e5 Step 3/6 : RUN go build -o /app main.go ---> Running in e000c7b4f4cb ---> 7094a3071df2 Step 4/6 : FROM alpine:3.7 ---> 9bea9e12e381 Step 5/6 : CMD ["./app"] ---> Using cache ---> 3ee2781f0277 Step 6/6 : COPY --from=builder /app . ---> eeeb9037a58f Successfully built eeeb9037a58f Successfully tagged 33aee612daa48982cc4b37f313762ef9:latest The push refers to repository [[account_id]].dkr.ecr.[region].amazonaws.com/k8s-skaffold/skaffold-example] a93eb3ea645d: Preparing d6da3c54c8f3: Preparing d6da3c54c8f3: Layer already exists a93eb3ea645d: Pushed eeeb9037a58fca0c94555b46b7a03ccf93d1da503f8e8d0d30c26e844af7df7c: digest: sha256:4d61b9ab9a0fd106f55dc2f041c170926a5d8e69a3d7c787c28583e13b1f642d size: 738 Build complete in 5.363027368s Starting test... Test complete in 7.512µs Starting deploy... kubectl client version: 1.10 kubectl version 1.12.0 or greater is recommended for use with skaffold pod "getting-started" configured Deploy complete in 778.911883ms Watching for changes every 1s... [getting-started] Hello world!2 [getting-started] Hello world!2 [getting-started] Hello world!2
無事に変更を検知して表示されるメッセージが切り替わりました!
アプリケーションだけではなく、マニフェストファイルの変更も検知してくれます、skaffold.yaml
の13行目でk8s-*
を条件に検索しています。
下記のファイルを作成してください。
apiVersion: v1 kind: Pod metadata: name: getting-started2 spec: containers: - name: getting-started2 image: [account_id].dkr.ecr.[region].amazonaws.com/k8s-skaffold/skaffold-example
マニフェストファイルの追加を検知してデプロイが走りました。Dockerイメージは同じ物なのでビルドは発生しません。
Starting deploy... kubectl client version: 1.10 kubectl version 1.12.0 or greater is recommended for use with skaffold pod "getting-started2" created Deploy complete in 4.403612632s Watching for changes every 1s... [getting-started] Hello world!2 [getting-started] Hello world!2 [getting-started2] Hello world!2 [getting-started2] Hello world!2 [getting-started2] Hello world!2 [getting-started] Hello world!2 [getting-started2] Hello world!2 [getting-started] Hello world!2
kubectlで起動中のPodsを確認してみました。2つ起動していることを確認できます。
$ kubectl get pods NAME READY STATUS RESTARTS AGE getting-started 1/1 Running 0 6s getting-started2 1/1 Running 0 6s
あとがき
Skaffoldを使うと、変更後に即座にデプロイまで行われて開発を加速できそうだなと感じました。 今回のブログでは触れていませんが、Skaffoldはkomposeと連携してdocker-composeをマニフェストファイルに変換してデプロイまで行ってくれる機能もあります。docker-composeは触っているけどマニフェストファイルはまだ書いたことがない...という人には嬉しい機能ですね。